package script

import (
	"fmt"
	"game/core"
	"game/core/tools"
	"time"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/audio"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/solarlune/resolv"
)

// Game implements ebiten.Game interface.
type Game struct {
	Node           [][]core.ISprite
	Space          *resolv.Space
	lastUpdateTime time.Time
}

var GG *Game
var (
	err            error
	BgImg          *ebiten.Image
	BulletImg      *ebiten.Image
	EnemyImg       *ebiten.Image
	PlayerImg      *ebiten.Image
	EnemyBulletImg []*ebiten.Image
	BoomImg        []*ebiten.Image

	AudioPlayer *audio.Player
	BulletSound *audio.Player
)

//var Timer core.Timer = *core.GetTimer()

func init() {
	BgImg, _, err = ebitenutil.NewImageFromFile("assets/res/images/m2.jpg")
	if err != nil {
		panic(err)
	}
	EnemyImg, _, err = ebitenutil.NewImageFromFile("assets/res/images/enemy/enemy1.png")
	if err != nil {
		panic(err)
	}
	PlayerImg, _, err = ebitenutil.NewImageFromFile("assets/res/images/hero/hero01.png")
	if err != nil {
		panic(err)
	}
	BulletImg, _, err = ebitenutil.NewImageFromFile("assets/res/images/bullet/bullet1.png")
	if err != nil {
		panic(err)
	}

	EImg1, _, err := ebitenutil.NewImageFromFile("assets/res/images/bullet/enemy_bullet1.png")
	if err != nil {
		panic(err)
	}
	EImg2, _, err := ebitenutil.NewImageFromFile("assets/res/images/bullet/enemy_bullet2.png")
	if err != nil {
		panic(err)
	}
	EImg3, _, err := ebitenutil.NewImageFromFile("assets/res/images/bullet/enemy_bullet3.png")
	if err != nil {
		panic(err)
	}
	EnemyBulletImg = append(EnemyBulletImg, EImg1, EImg2, EImg3)
	BoomImg1, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom01.png")
	BoomImg2, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom02.png")
	BoomImg3, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom03.png")
	BoomImg4, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom04.png")
	BoomImg5, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom05.png")
	BoomImg6, _, err := ebitenutil.NewImageFromFile("assets/res/images/boom/boom06.png")
	BoomImg = append(BoomImg, BoomImg1, BoomImg2, BoomImg3, BoomImg4, BoomImg5, BoomImg6)

	go func() {
		BulletSound = tools.LoadingMp3("assets/res/audio/planeshoot0.mp3", false)
		BulletSound.SetVolume(1)
		//
		AudioPlayer = tools.LoadingMp3("assets/res/audio/bg.mp3", true)
		AudioPlayer.SetVolume(0.5)
		AudioPlayer.Play()
	}()
}
func NewGame() *Game {

	GG = &Game{}
	GG.Space = resolv.NewSpace(512, 768, 32, 32)
	//GG.Collision = resolv.NewRectangle(512/2, 768/2, 512, 768)

	solids := resolv.ShapeCollection{
		resolv.NewRectangleFromTopLeft(0, 0, 512, 16),
		resolv.NewRectangleFromTopLeft(0, 768-16, 512, 16),
		resolv.NewRectangleFromTopLeft(0, 16, 16, 768-16),
		resolv.NewRectangleFromTopLeft(512-16, 16, 16, 768-16),
	}
	solids.SetTags(core.TagWall)
	solids.ForEach(func(s resolv.IShape) bool {
		s.SetData(s)
		return true
	})

	//
	for i := 0; i < core.LayerMax; i++ {
		GG.Node = append(GG.Node, []core.ISprite{})
	}

	GG.Space.Add(solids...)

	//
	AddNode(GG, core.LayerMap, NewBg())
	AddNode(GG, core.LayerPlayer, NewPlayer())

	//
	go func() {
		for {
			fmt.Println("FPS: ", ebiten.ActualFPS())
			fmt.Println("Ticks: ", ebiten.ActualTPS())
			fmt.Println("len: ", len(GG.Node[core.LayerEnemy]))
			fmt.Println("len2: ", len(GG.Node[core.LayerBullet]))
			fmt.Println("len3: ", len(GG.Space.Shapes()))
			time.Sleep(time.Second)

		}
	}()

	return GG
}

// Update proceeds the game state.
// Update is called every tick (1/60 [s] by default).

func (g *Game) Update() error {
	// 检查是否需要重新加载音频文件
	// if !AudioPlayer.IsPlaying() {
	// 	AudioPlayer.Rewind()
	// }

	// 计算 dt
	// 初始化 lastUpdateTime
	if g.lastUpdateTime.IsZero() {
		g.lastUpdateTime = time.Now()
	}
	// 获取当前时间
	now := time.Now()
	// 计算 dt
	dt := now.Sub(g.lastUpdateTime).Seconds()
	// 更新 lastUpdateTime 为当前时间
	g.lastUpdateTime = now

	// Write your game's logical update.
	for k1, layerNode := range g.Node {
		//newNode := []core.ISprite{}
		var keepNodes []core.ISprite
		for _, node := range layerNode {
			//如果有删除标签，则删除
			if node.GetDestory() {
				//删除碰撞
				g.Space.Remove(node.GetCollision())
				//删除节点
				// if k2 >= 0 && k2 < len(g.Node[k1]) {
				// 	g.Node[k1] = append(g.Node[k1][:k2], g.Node[k1][k2+1:]...)
				// }
			} else {
				//newNode = append(newNode, node)
				if err := node.Update(dt); err != nil {
					return err
				}
				for _, v := range node.GetTimer() {
					v.UpdateTimer(dt)
				}
				keepNodes = append(keepNodes, node)
				// if node.GetProjectileMovement() != nil {
				// 	node.GetProjectileMovement().Update(dt)
				// }
			}
		}
		g.Node[k1] = keepNodes
		//g.Node[k1] = newNode
	}

	//Timer.UpdateTimer(dt)

	return nil
}

// Draw draws the game screen.
// Draw is called every frame (typically 1/60[s] for 60Hz display).
func (g *Game) Draw(screen *ebiten.Image) {
	// Write your game's rendering.
	for _, layerNode := range g.Node {
		for _, node := range layerNode {
			node.Draw(screen)
		}
	}
	//g.DebugDraw(screen, g.Space)
	//fmt.Println(g.Space.CellWidth())
	CommonDraw(screen, g.Space)
}

// Layout takes the outside size (e.g., the window size) and returns the (logical) screen size.
// If you don't have to adjust the screen size with the outside size, just return a fixed size.
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {

	return 512, 768
}

func (g *Game) DebugDraw(screen *ebiten.Image, space *resolv.Space) {

	for y := 0; y < space.Height(); y++ {

		for x := 0; x < space.Width(); x++ {

			cell := space.Cell(x, y)
			fmt.Println(cell)
			// cw := float32(space.CellWidth())
			// ch := float32(space.CellHeight())
			// cx := float32(cell.X) * cw
			// cy := float32(cell.Y) * ch

			// drawColor := color.RGBA{20, 20, 20, 255}

			// if cell.IsOccupied() {
			// 	drawColor = color.RGBA{255, 255, 0, 255}
			// }

			// vector.StrokeRect(screen, cx, cy, cx+cw, cy, 2, drawColor, false)

			// vector.StrokeRect(screen, cx+cw, cy, cx+cw, cy+ch, 2, drawColor, false)

			// vector.StrokeRect(screen, cx+cw, cy+ch, cx, cy+ch, 2, drawColor, false)

			// vector.StrokeRect(screen, cx, cy+ch, cx, cy, 2, drawColor, false)
		}

	}

}

func AddNode(g *Game, layer int, node ...core.ISprite) {

	g.Node[layer] = append(g.Node[layer], node...)
	for _, n := range node {
		if coll := n.GetCollision(); coll != nil {
			coll.SetData(n)
			g.Space.Add(n.GetCollision())
		}
	}

	// todo 排序优化，初始化时排序，分组
	// sort.Slice(g.Node, func(i, j int) bool {
	// 	return g.Node[i].GetLayer() < g.Node[j].GetLayer()
	// })
}
